/**
 * (C) 2007 Stefan Arentz & Polar Rose AB
 *
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.polarrose.jetty.deployer.maven;

import org.mortbay.component.AbstractLifeCycle;
import org.mortbay.jetty.handler.ContextHandlerCollection;
import org.mortbay.jetty.webapp.WebAppContext;
import org.mortbay.log.Log;

import java.io.File;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;

/**
 * @author Stefan Arentz
 */

public class MavenDeployer extends AbstractLifeCycle
{
    public final static String NAME = "MavenDeployer";    

    private ContextHandlerCollection contexts;
    private String repositoryUrl;
    private String artifactName;
    private String contextPath;
    private long scanInterval;

    private String lastSeenSnapshotTimestamp = "";
    private String lastSeenSnapshotBuildNumber = "";

    private MavenArtifact artifact;
    private MavenRepositoryScanner repositoryScanner;
    private WebAppContext wac;

    //

    public MavenDeployer() throws Exception
    {
    }

    //

    public void setContexts(ContextHandlerCollection contexts)
    {
        this.contexts = contexts;
    }

    public void setRepositoryUrl(String repositoryUrl)
    {
        this.repositoryUrl = repositoryUrl;
    }

    public void setArtifactName(String artifactName)
    {
        this.artifactName = artifactName;
    }

    public void setContextPath(String contextPath)
    {
        this.contextPath = contextPath;
    }

    public void setScanInterval(long scanInterval)
    {
        this.scanInterval = scanInterval;
    }

    //
    
    protected void doStart() throws Exception
    {
        if (artifactName == null) {
            throw new IllegalArgumentException("Invalid artifactName: " + artifactName);
        }

        if (repositoryUrl == null) {
            throw new IllegalArgumentException("Invalid repositoryUrl: " + repositoryUrl);
        }

        artifact = new MavenArtifact(artifactName);

        if (!artifact.getVersion().endsWith("-SNAPSHOT")) {
            throw new IllegalArgumentException("Only -SNAPSHOT versions are supported");
        }

        if (contextPath == null) {
            contextPath = "/" + artifact.getArtifactId();
        }

        Log.info("Starting Maven Deployer; scanning for " + artifact + " on " + repositoryUrl + " every " + scanInterval + " seconds");
        
        repositoryScanner = new MavenRepositoryScanner();
        repositoryScanner.start();
    }

    protected void doStop() throws Exception
    {
        Log.info("Stopping Maven Deployer");
        repositoryScanner.stop();
    }

    //

    public void reload(String snapshotTimestamp, String snapshotBuildNumber) throws Exception
    {
        if (!lastSeenSnapshotTimestamp.equals(snapshotTimestamp) || !lastSeenSnapshotBuildNumber.equals(snapshotBuildNumber))
        {
            Log.info("Reloading " + artifact);

            File warFile = null;

            try {
                undeployWebApp();
                warFile = MavenUtil.downloadArtifact(new URL(repositoryUrl), artifact, snapshotTimestamp, snapshotBuildNumber);
                deployWebApp(warFile);
            } catch (Exception e) {
                Log.info("Cannot reload " + artifact + ": " + e.getMessage(), e); 
            } finally {
                if (warFile != null) {
                    warFile.delete();
                }
            }

            lastSeenSnapshotTimestamp = snapshotTimestamp;
            lastSeenSnapshotBuildNumber = snapshotBuildNumber;
        }
    }

    //

    private void undeployWebApp() throws Exception
    {
        if (wac != null)
        {
            Log.info("Undeploying " + contextPath);

            wac.stop();
            contexts.removeHandler(wac);
            wac = null;
        }
    }

    private void deployWebApp(File warFile) throws Exception
    {
        if (wac != null) {
            throw new IllegalStateException("Trying to deploy the web app while it is already deployed");
        }

        Log.info("Deploying " + warFile.getPath() + " on " + contextPath);

        wac = new WebAppContext();
        wac.setContextPath(contextPath);
        wac.setExtractWAR(true);
        wac.setWar(warFile.getPath());

        contexts.addHandler(wac);

        wac.start();
    }

    //

    public class MavenRepositoryScanner
    {
        private boolean running;
        private Timer timer;
        private TimerTask task;

        //

        public synchronized void start()
        {
            if (!running) {
                running = true;
                task = new RepositoryScannerTask();
                timer = new Timer();
                timer.scheduleAtFixedRate(task, 1000L, scanInterval * 1000);
            }
        }

        public synchronized void stop()
        {
            if (running) {
                running = false;
                task.cancel();
            }
        }

        //

        private void scan()
        {
            MavenMetaData artifactMetaData = null;
            
            try {
                artifactMetaData = MavenUtil.downloadMavenMetaData(new URL(repositoryUrl), artifact);
            } catch (Exception e) {
                Log.warn("Cannot download Maven MetaData for " + artifact + ": " + e.getMessage());
            }

            if (artifactMetaData != null) {
                try {
                    reload(artifactMetaData.getSnapshotTimestamp(), artifactMetaData.getSnapshotBuildNumber());
                } catch (Exception e) {
                    Log.warn("Cannot deploy " + artifact + ": " + e.getMessage());
                }
            }
        }

        //

        class RepositoryScannerTask extends TimerTask
        {
            public void run() {
                scan();
            }
        }
    }
}
